package apiroute

import (
	"fmt"
	"strconv"
	"strings"
)

func formatGeoJSON(legs *[]RouteLeg) *[]GeoJSONFeature {
	featurePoints := []GeoJSONFeature{}
	featureLineStringGeometry := GeoJSONGeometryLineString{
		Type:        "LineString",
		Coordinates: [][2]float64{},
	}

	// Generate (1) GeoJSON Line Feature to aggregate 'featureLineString'
	for legI, leg := range *legs {
		featureLineStringGeometry.Coordinates = append(
			featureLineStringGeometry.Coordinates,
			legToCoordinates(&leg, legI)...,
		)
	}

	// Generate (n+) GeoJSON Point Features
	for legI, leg := range *legs {
		featurePoints = append(
			featurePoints,
			legToGeoJSONFeaturePoints(&leg, legI)...,
		)
	}

	returnFeatures := append(
		featurePoints,
		GeoJSONFeature{
			Type:       "Feature",
			Properties: map[string]string{"name": "Route"},
			Geometry:   featureLineStringGeometry,
		},
	)

	return &returnFeatures
}

func legToCoordinates(leg *RouteLeg, legI int) [][2]float64 {
	coords := [][2]float64{}
	coords = append(coords, stringLatlonToLonLatArray(leg.LegFromCoords))
	if leg.LegType == "trip" {
		for _, tripStop := range *leg.TripStops {
			coords = append(coords, stringLatlonToLonLatArray(tripStop.Coords))
		}
	}
	coords = append(coords, stringLatlonToLonLatArray(leg.LegToCoords))
	return coords
}

func legToGeoJSONFeaturePoints(leg *RouteLeg, legI int) []GeoJSONFeature {
	points := []GeoJSONFeature{}

	if leg.LegType == "walk" && legI == 0 { // Initial walk from origin
		points = append(points, GeoJSONFeature{
			Type: "Feature",
			Properties: map[string]string{
				"icon": "walk",
				"name": fmt.Sprintf(
					"Walk from origin (%s): %s → %s (%s)",
					leg.LegFromCoords,
					leg.LegBeginTime,
					leg.LegEndTime,
					leg.LegDuration,
				),
			},
			Geometry: GeoJSONGeometryPoint{
				Type:        "Point",
				Coordinates: stringLatlonToLonLatArray(leg.LegFromCoords),
			},
		})
	} else if leg.LegType == "walk" && legI != 0 { // Final walk to destination
		points = append(points, GeoJSONFeature{
			Type: "Feature",
			Properties: map[string]string{
				"icon": "walk",
				"name": fmt.Sprintf(
					"Walk to destination (%s): %s → %s (%s)",
					leg.LegToCoords,
					leg.LegBeginTime,
					leg.LegEndTime,
					leg.LegDuration,
				),
			},
			Geometry: GeoJSONGeometryPoint{
				Type:        "Point",
				Coordinates: stringLatlonToLonLatArray(leg.LegToCoords),
			},
		})
	} else if leg.LegType == "trip" {
		points = append(points, GeoJSONFeature{
			Type: "Feature",
			Properties: map[string]string{
				"icon": "board",
				"name": fmt.Sprintf(
					"Board trip (%s toward %s): %s → %s (%s)",
					*leg.TripRoute,
					*leg.TripRouteHeadsign,
					leg.LegBeginTime,
					leg.LegEndTime,
					leg.LegDuration,
				),
			},
			Geometry: GeoJSONGeometryPoint{
				Type:        "Point",
				Coordinates: stringLatlonToLonLatArray(leg.LegFromCoords),
			},
		})
		for i, tripStop := range *leg.TripStops {
			if i == 0 {
				continue
			}
			points = append(points, GeoJSONFeature{
				Type: "Feature",
				Properties: map[string]string{
					"icon": "stop",
					"name": fmt.Sprintf("Trip stop (%s) at %s", tripStop.Name, tripStop.Time),
				},
				Geometry: GeoJSONGeometryPoint{
					Type:        "Point",
					Coordinates: stringLatlonToLonLatArray(tripStop.Coords),
				},
			})
		}
	} else if leg.LegType == "transfer" {
		points = append(points, GeoJSONFeature{
			Type: "Feature",
			Properties: map[string]string{
				"icon": "board",
				"name": "Transfer",
			},
			Geometry: GeoJSONGeometryPoint{
				Type:        "Point",
				Coordinates: stringLatlonToLonLatArray(leg.LegToCoords),
			},
		})
	}

	return points
}

func stringLatlonToLonLatArray(coordsStr string) [2]float64 {
	items := strings.Split(coordsStr, ",")
	latStr := strings.TrimSpace(items[0])
	lonStr := strings.TrimSpace(items[1])
	lat, _ := strconv.ParseFloat(latStr, 64)
	lon, _ := strconv.ParseFloat(lonStr, 64)
	return [2]float64{lon, lat}
}
